const config = require('../config.json');
const types = require('./types');
var _ = require('lodash');

const DataType = types.DataType;
const SheetType = types.SheetType;

function parseJsonObject(data) {
    var evil = eval;
    return evil("(" + data + ")");
}

// class StringBuffer {
//   constructor(str) {
//     this._str_ = [];
//     if (str) {
//       this.append(str);
//     }
//   }

//   toString() {
//     return this._str_.join("");
//   }

//   append(str) {
//     this._str_.push(str);
//   }
// }


/**
 * 解析workbook中所有sheet的设置
 * @param {*} workbook 
 */
function parseSettings(workbook) {

    /**
     * settings's schema
     * {
        type: SheetType.NORMAL,
        head: [{
            name: "json-key(column name)",
            type: "number/string/bool/data/id/[]/{}",
          }]
        ,
        slaves: [],
        master: {
          name: '',
          type: DataType.OBJECT
        },
      } 
     */

    let settings = {};
    for (let sheetIdx = 0; sheetIdx < workbook.length; sheetIdx++) {
        const sheet = workbook[sheetIdx];

        // }
        // workbook.forEach(sheet => {

        //叹号开头的sheet不输出
        if (sheet.name.startsWith('!')) {
            continue;
        }

        let hasSlave = sheet.name.indexOf('@') >= 0;
        let sheet_name = sheet.name;

        let sheet_setting = {
            type: SheetType.NORMAL,
            master: null,
            slaves: [],
            head: [],
            desc: [],
        };

        if (hasSlave) {
            sheet_setting.type = SheetType.SLAVE;
            let pair = sheet_name.split('@');
            sheet_name = pair[0].trim();
            sheet_setting.master = pair[1].trim();
            let slave = {
                name: sheet_name,
                desc: "",
                type: DataType.UNKNOWN,
                data: {},
            };
            let is_array = _.find(sheet.data[config.head], item => {
                return item === DataType.IDS;
            });
            //从表的字段描述必须填在从表的[0,0]位置
            slave.desc = sheet.data[0][0];
            slave.type = is_array ? DataType.ARRAY_STR : DataType.OBJECT_STR
            slave.data = {
                name: [],
                desc: [],
                type: [],
            }
            for (let i = 1; i < sheet.data[config.head].length; i++) {
                const name = sheet.data[config.head - 1][i];
                const desc = sheet.data[config.head - 2][i];
                const type = sheet.data[config.head][i];
                slave.data.name.push(name)
                slave.data.desc.push(desc)
                slave.data.type.push(type)
            }
            if (settings[sheet_setting.master]) {
                settings[sheet_setting.master].slaves.push(slave);
            }
        }

        let head_row = sheet.data[config.head - 1];
        if (head_row == null) {
            continue;
        }
        // //parsing head setting
        // head_row.forEach(cell => {

        //   cell = cell.toString();

        //   let head_setting = {
        //     name: cell,
        //     type: DataType.UNKNOWN,
        //   };

        //   if (cell.indexOf('#') !== -1) {
        //     let pair = cell.split('#');
        //     let name = pair[0].trim();
        //     let type = pair[1].trim();

        //     head_setting.name = name;
        //     head_setting.type = type;

        //     if (!slave && type === DataType.ID) {
        //       sheet_setting.type = SheetType.MASTER;
        //     }
        //   }
        //   sheet_setting.head.push(head_setting);
        // });
        //@jdk parsing head setting
        for (let i = 0; i < head_row.length; i++) {
            if (head_row[i] == null) {
                continue;
            }
            const cell = head_row[i].toString();

            let head_setting = {
                name: cell,
                type: DataType.UNKNOWN,
            };

            const type = sheet.data[config.head][i] ? sheet.data[config.head][i].toLowerCase() : DataType.UNKNOWN;
            head_setting.type = type;

            if (!hasSlave && head_setting.type === DataType.ID) {
                sheet_setting.type = SheetType.MASTER;
            }
            sheet_setting.head.push(head_setting);
        }
        let desc_row = sheet.data[config.desc - 1];

        //parsing desc setting
        desc_row.forEach(cell => {
            cell = cell.toString();
            sheet_setting.desc.push(cell);
        });

        settings[sheet_name] = sheet_setting;
    }
    // });

    return settings;
}


/**
 * 解析一个表(sheet)
 *
 * @param sheet 表的原始数据
 * @param setting 表的设置
 * @return Array or Object
 */
function parseSheet(sheet, setting) {

    let firstIdx = config.first_idx - 1;
    let result = [];

    console.log('  * sheet:', sheet.name);

    if (!setting) {
        return null;
    }
    if (setting.type === SheetType.MASTER) {
        result = {};
    }

    for (let i_row = firstIdx; i_row < sheet.data.length; i_row++) {

        let row = sheet.data[i_row];
        let parsed_row = parseRow(row, i_row, setting.head);
        if (parsed_row == null) {
            continue
        }
        if (setting.type === SheetType.MASTER) {

            let id_cell = _.find(setting.head, item => {
                return item.type === DataType.ID || item.type === DataType.IDS;
            });

            if (!id_cell) {
                throw `在表${sheet.name}中获取不到id列`;
            }
            if (parsed_row[id_cell.name] != null) {
                result[parsed_row[id_cell.name]] = parsed_row;
            }

        } else {
            result.push(parsed_row);
        }
    }

    return result;
}

/**
 * 解析一行
 * @param {*} row 
 * @param {*} rowIndex 
 * @param {*} head 
 */
function parseRow(row, rowIndex, head) {

    let result = {};
    let id;

    for (let index = 0; index < head.length; index++) {
        let cell = row[index];

        let name = head[index].name;
        let type = head[index].type;

        if (name.startsWith('!')) {
            continue;
        }

        if (cell == null || cell == undefined) {
            if (name == 'id') {
                return null
            }
            result[name] = null;
            continue;
        }
        if (type === DataType.NUMBER || type === DataType.INT || type === DataType.DOUBLE || type === DataType.FLOAT) {
            //+xxx.toString() '+' means convert it to number
            if (isNumber(cell)) {
                result[name] = Number(cell);
            } else {
                console.warn("type error at [" + rowIndex + "," + index + "]," + cell + " is not a number");
            }
        } else if (type === DataType.ID || type == DataType.IDS) {
            id = cell + '';
            result[name] = id;
        } else if (type === DataType.STRING) {
            if (cell.toString().startsWith('"')) {
                result[name] = parseJsonObject(cell);
            } else {
                result[name] = cell.toString();
            }
        } else if (type === DataType.BOOL || type === DataType.BOOLEAN) {
            result[name] = toBoolean(cell);
        } else if (type === DataType.ARRAY || type === DataType.ARRAY_STR || type === DataType.ARRAY_STRING || type === DataType.ARRAY_NUMBER || type === DataType.ARRAY_BOOL || type === DataType.ARRAY_BOOLEAN) {
            if (!cell.toString().startsWith('[')) {
                cell = `[${cell}]`;
            }
            result[name] = parseJsonObject(cell);
        } else if (type === DataType.OBJECT || type === DataType.OBJECT_STR || type.length > 0) {
            if (!cell.toString().startsWith('{')) {
                cell = `{${cell}}`;
            }
            result[name] = parseJsonObject(cell);
        } else if (type === DataType.DATE) {
            if (isNumber(cell)) {
                //xlsx's bug!!!
                result[name] = numdate(cell);
            } else {
                result[name] = cell.toString();
            }
        } else if (type === DataType.UNKNOWN) {
            if (isNumber(cell)) {
                result[name] = Number(cell);
            } else if (isBoolean(cell)) {
                result[name] = toBoolean(cell);
            } else {
                result[name] = cell;
            }
        } else {
            console.log('无法识别的类型:', '[' + rowIndex + ',' + index + ']', cell, typeof (cell));
        }

    }

    return result;
}

/**
 * convert value to boolean.
 */
function toBoolean(value) {
    return value.toString().toLowerCase() === 'true';
}

/**
 * is a number.
 */
function isNumber(value) {

    if (typeof value === 'number') {
        return true;
    }

    if (value) {
        return !isNaN(+value.toString());
    }

    return false;
}

/**
 * boolean type check.
 */
function isBoolean(value) {

    if (typeof (value) === "undefined") {
        return false;
    }

    if (typeof value === 'boolean') {
        return true;
    }

    let b = value.toString().trim().toLowerCase();

    return b === 'true' || b === 'false';
}

//fuck node-xlsx's bug
var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
// var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
// function datenum(v, date1904) {
// 	var epoch = v.getTime();
// 	if(date1904) epoch -= 1462*24*60*60*1000;
// 	return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
// }

function numdate(v) {
    var out = new Date();
    out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
    return out;
}
//fuck over


module.exports = {

    parseSettings: parseSettings,

    parseWorkbook: function (workbook, settings) {

        // console.log('settings >>>>>', JSON.stringify(settings, null, 2));

        let parsed_workbook = {};

        for (let sheetIdx = 0; sheetIdx < workbook.length; sheetIdx++) {
            const sheet = workbook[sheetIdx];

            // }
            // workbook.forEach(sheet => {

            // if (sheet.name.startsWith('!')) {
            if (sheet.name.startsWith('!') || sheet.name.startsWith('！') || sheet.name.startsWith('！') || sheet.name.startsWith('！')) {
                continue;
            }

            let sheet_name = sheet.name;

            let isSlave = sheet_name.indexOf('@') >= 0;

            if (isSlave) {
                sheet_name = sheet_name.split('@')[0].trim();
            }

            let sheet_setting = settings[sheet_name];
            let parsed_sheet = parseSheet(sheet, sheet_setting);

            parsed_workbook[sheet_name] = parsed_sheet;

        }
        // });

        for (let name in settings) {
            if (settings[name].type === SheetType.MASTER) {

                let master_sheet = parsed_workbook[name];

                settings[name].slaves.forEach(slave => {

                    let slave_setting = settings[slave.name];
                    let slave_sheet = parsed_workbook[slave.name];
                    if (slave_setting == null && slave_sheet == null) {
                        return;
                    }
                    let key_cell = _.find(slave_setting.head, item => {
                        return item.type === DataType.ID || item.type === DataType.IDS;
                    });

                    //slave 表中所有数据
                    for (let i = 0; i < slave_sheet.length; i++) {
                        const row = slave_sheet[i];
                        let id = row[key_cell.name];
                        if (!id || !master_sheet[id]) {
                            continue;
                        }
                        delete row[key_cell.name];
                        if (key_cell.type === DataType.IDS) { //array
                            master_sheet[id][slave.name] = master_sheet[id][slave.name] || [];
                            master_sheet[id][slave.name].push(row);
                        } else { //hash
                            master_sheet[id][slave.name] = row;
                        }
                    }
                    delete parsed_workbook[slave.name];
                });
            }
        }

        return parsed_workbook;
    }
};